﻿#include "framework.h"
#include "Const.h"
#include "PictureCtrl.h"

// 函数声明
static void PictureCtrl_Delete(PictureCtrl*);
static void PictureCtrl_GetPosition(PictureCtrl*, POINT*);
static BOOL PictureCtrl_IsOnOriginalPosition(PictureCtrl*);
static void PictureCtrl_OnLButtonUp(PictureCtrl*);
static void PictureCtrl_OnPaint(PictureCtrl*);
static LRESULT CALLBACK PictureCtrl_Proc(HWND, UINT, WPARAM, LPARAM);
static void PictureCtrl_SetBorder(PictureCtrl*, BOOL);

/**
 * 创建图片控件。
 * @param hasBorder 有边框则为 TRUE，否则为 FALSE
 * @param id        id
 * @param imageDC   图片保存者
 * @param parent    父窗口
 * @param rect      位置、尺寸
 * @return 图片控件
 */
PictureCtrl* New_PictureCtrl(BOOL hasBorder, UINT id, HDC imageDC, HWND parent, RECT rect) {
    HDC dc = NULL;
    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;
    PictureCtrl* pictureCtrl = (PictureCtrl*)calloc(1, sizeof(PictureCtrl));
    WNDCLASSEX wndClass = { sizeof(WNDCLASSEX) };

    // 注册控件窗口类
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = PictureCtrl_Proc;
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.lpszClassName = PCITURECTRL_NAME;
    RegisterClassEx(&wndClass);

    // 初始化数据
    pictureCtrl->mHandle = CreateWindowEx(0, PCITURECTRL_NAME, NULL, WS_CHILD | WS_VISIBLE, 
        rect.left, rect.top, width, height, parent, (HMENU)id, NULL, NULL);
    SetWindowLongPtr(pictureCtrl->mHandle, GWLP_USERDATA, (LONG_PTR)pictureCtrl);
    pictureCtrl->mHasBorder = hasBorder;
    pictureCtrl->mId = id;
    dc = GetDC(pictureCtrl->mHandle);
    pictureCtrl->mImage = CreateCompatibleBitmap(dc, width, height);
    pictureCtrl->mImageDC = CreateCompatibleDC(dc);
    SelectObject(pictureCtrl->mImageDC, pictureCtrl->mImage);
    BitBlt(pictureCtrl->mImageDC, 0, 0, width, height, imageDC, rect.left, rect.top, SRCCOPY);
    pictureCtrl->mOriginalPosition.x = rect.left;
    pictureCtrl->mOriginalPosition.y = rect.top;
    pictureCtrl->mSize = width;

    // 初始化函数
    pictureCtrl->deleteSelf = PictureCtrl_Delete;
    pictureCtrl->getPosition = PictureCtrl_GetPosition;
    pictureCtrl->isOnOriginalPosition = PictureCtrl_IsOnOriginalPosition;
    pictureCtrl->setBorder = PictureCtrl_SetBorder;

    return pictureCtrl;
}

/**
 * 删除图片控件。
 * @param pictureCtrl   图片控件
 */
static void PictureCtrl_Delete(PictureCtrl* pictureCtrl) {
    DeleteObject(pictureCtrl->mImage);
    DeleteDC(pictureCtrl->mImageDC);
    DestroyWindow(pictureCtrl->mHandle);
    free(pictureCtrl);
}

/**
 * 获取当前位置。
 * @param pictureCtrl   图片控件
 * @param position      当前位置
 */
static void PictureCtrl_GetPosition(PictureCtrl* pictureCtrl, POINT* position) {
    HWND parent = GetParent(pictureCtrl->mHandle);
    RECT rect = { 0 };

    GetWindowRect(pictureCtrl->mHandle, &rect);
    position->x = rect.left;
    position->y = rect.top;
    ScreenToClient(parent, position);
}

/**
 * 判断自身是否在初始位置。
 * @param pictureCtrl   图片控件
 * @return 在初始位置则返回 TRUE，否则返回 FALSE
 */
static BOOL PictureCtrl_IsOnOriginalPosition(PictureCtrl* pictureCtrl) {
    POINT currentPosition = { 0 };
    PictureCtrl_GetPosition(pictureCtrl, &currentPosition);
    return currentPosition.x == pictureCtrl->mOriginalPosition.x && currentPosition.y == pictureCtrl->mOriginalPosition.y;
}

/**
 * 鼠标左键抬起事件的响应函数。
 * @param pictureCtrl   图片控件
 */
static void PictureCtrl_OnLButtonUp(PictureCtrl* pictureCtrl) {
    HWND parent = GetParent(GetParent(pictureCtrl->mHandle));
    SendMessage(parent, WM_COMMAND, pictureCtrl->mId, (LPARAM)pictureCtrl->mHandle);
}

/**
 * 重绘事件的响应函数。
 * @param pictureCtrl   图片控件
 */
static void PictureCtrl_OnPaint(PictureCtrl* pictureCtrl) {
    PAINTSTRUCT ps = { 0 };
    RECT clientRect = { 0 };

    GetClientRect(pictureCtrl->mHandle, &clientRect);
    BeginPaint(pictureCtrl->mHandle, &ps);
    BitBlt(ps.hdc, 0, 0, clientRect.right, clientRect.bottom, pictureCtrl->mImageDC, 0, 0, SRCCOPY);
    if (pictureCtrl->mHasBorder) {
        DrawEdge(ps.hdc, &clientRect, EDGE_RAISED, BF_RECT);
    }
    EndPaint(pictureCtrl->mHandle, &ps);
}

/**
 * 窗口过程函数。
 * @param window    窗口
 * @param message   事件
 * @param wParam    事件相关信息1
 * @param lParam    事件相关信息2
 * @return 事件在此被处理则返回 TRUE，否则返回 FALSE
 */
static LRESULT CALLBACK PictureCtrl_Proc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
    PictureCtrl* pictureCtrl = (PictureCtrl*)GetWindowLongPtr(window, GWLP_USERDATA);

    switch (message) {
        case WM_LBUTTONUP:
            PictureCtrl_OnLButtonUp(pictureCtrl);
            return TRUE;
        case WM_PAINT:
            PictureCtrl_OnPaint(pictureCtrl);
            return TRUE;
    }
    return DefWindowProc(window, message, wParam, lParam);
}

/**
 * 设置边框。
 * @param pictureCtrl   图片控件
 * @param hasBorder     有边框则为 TRUE，否则为 FALSE
 */
static void PictureCtrl_SetBorder(PictureCtrl* pictureCtrl, BOOL hasBorder) {
    if (pictureCtrl->mHasBorder == hasBorder) {
        return;
    }
    pictureCtrl->mHasBorder = hasBorder;
    InvalidateRect(pictureCtrl->mHandle, NULL, FALSE);
}
